﻿using System;
using System.Data;
using System.IO;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Documents;
using Lucene.Net.Index;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using Lucene.Net.Store;
using Persistencia;
using Directory = Lucene.Net.Store.Directory;

namespace Indexador
{
    class IndexadorLucene : IEstrategiaIndexado
    {
        #region "variable"
        private static Directory _directory;
        private static Analyzer _analyzer;
        #endregion


        #region Implementation of IEstrategiaIndexado

        /// <summary>
        /// Agrega el archivo al indexador Lucene.NET.
        /// </summary>
        /// <param name="pathIndexador"></param>
        /// <param name="fileSystemElement"></param>
        public void IndexarElemento(String pathIndexador, FileSystemElement fileSystemElement)
        {
            IndexWriter indexWriter=null;
            try
            {
                Document document = InicializarIndexadorLucene(pathIndexador, out indexWriter);
                InicializarDocumento(fileSystemElement, document);
                indexWriter.AddDocument(document);
                indexWriter.Close();
            }
            catch (Exception e)
            {
                throw new Exception("Se produjo un error al indexar el archivo.");
            }
            finally
            {
                if (indexWriter != null) indexWriter.Close();
            }
        }

        /// <summary>
        /// Inicializa las variables con la que se debe manejar el indexador
        /// </summary>
        /// <param name="pathIndexador"></param>
        public void InicializarIndexador(String pathIndexador)
        {
            try
            {
                FileInfo indexador = new FileInfo(pathIndexador);
                bool directoryExists = indexador.Directory.Exists;
                bool createDirectory = !directoryExists;
                if (_directory == null)
                {
                    _directory = FSDirectory.GetDirectory(indexador, createDirectory);
                }
                if (_analyzer == null)
                {
                    _analyzer = new StandardAnalyzer();
                }
            }
            catch (Exception)
            {
                throw new Exception("Hubo un error al inicializar el indexador.");
            }
        }

        /// <summary>
        /// Dado un termino se realiza la busqueda del mismo en el indexador
        /// </summary>
        /// <param name="pathIndexador"></param>
        /// <param name="termino"></param>
        /// <returns></returns>
        public DataSet Realizarbusqueda(String pathIndexador, String termino)
        {
            IndexSearcher indexSearcher = null;
            try
            {
                indexSearcher = GetIndexSearcher(pathIndexador);

                BooleanQuery booleanQuery = new BooleanQuery();
                Query queryNombre = new TermQuery(new Term("Nombre", termino));
                Query queryPath = new TermQuery(new Term("Path", termino));
                Query queryExtension = new TermQuery(new Term("Extension", termino));
                Query queryFecha = new TermQuery(new Term("Fecha", termino));
                booleanQuery.Add(queryNombre, BooleanClause.Occur.SHOULD);
                booleanQuery.Add(queryPath, BooleanClause.Occur.SHOULD);
                booleanQuery.Add(queryExtension, BooleanClause.Occur.SHOULD);
                booleanQuery.Add(queryFecha, BooleanClause.Occur.SHOULD);
                Hits hits = indexSearcher.Search(booleanQuery, Sort.RELEVANCE);

                indexSearcher.Close();
                _directory.Close();

                int numberOfResults = hits.Length();
                DataSet resultado = null;
                if (numberOfResults > 0)
                {
                    TablaResultados(hits, out resultado);
                }
                return resultado;
            }
            catch (Exception)
            {
                return new DataSet();
            }
            finally
            {
                if (indexSearcher != null) indexSearcher.Close();
            }
        }

        /// <summary>
        /// Modificar un archivo del indexador. Como no hay modificacion directa se elimina el archivo que estaba
        /// y se crea uno nuevo con los datos del archivo modificado.
        /// </summary>
        /// <param name="pathIndexador"></param>
        /// <param name="fseOriginal"></param>
        /// <param name="fseNuevo"></param>
        public void ModificarElemento(String pathIndexador, FileSystemElement fseOriginal, FileSystemElement fseNuevo)
        {
            try
            {
                EliminarElemento(pathIndexador, fseOriginal);
                IndexarElemento(pathIndexador, fseNuevo);
            }
            catch (Exception)
            {
                throw new Exception("Error al modificar el archivo indexado.");
            }
        }

        /// <summary>
        /// Eliminar un archivo del indexador
        /// </summary>
        /// <param name="pathIndexador"></param>
        /// <param name="fileSystemElement"></param>
        public void EliminarElemento(String pathIndexador, FileSystemElement fileSystemElement)
        {
            IndexReader reader = null;
            IndexSearcher searcher = null;
            try
            {
                FileInfo file = new FileInfo(fileSystemElement.Path);
                if (file.Exists)
                {
                    reader = IndexReader.Open(pathIndexador);
                    searcher = new IndexSearcher(reader);
                    BooleanQuery booleanQuery = new BooleanQuery();
                    Query queryNombre = new TermQuery(new Term("Nombre", fileSystemElement.Nombre));
                    Query queryPath = new TermQuery(new Term("Path", fileSystemElement.Path));
                    Query queryExtension = new TermQuery(new Term("Extension", fileSystemElement.Extension));
                    Query queryFecha = new TermQuery(new Term("Fecha", fileSystemElement.Fecha_Creacion.ToString()));
                    booleanQuery.Add(queryNombre, BooleanClause.Occur.MUST);
                    booleanQuery.Add(queryPath, BooleanClause.Occur.MUST);
                    booleanQuery.Add(queryExtension, BooleanClause.Occur.SHOULD);
                    booleanQuery.Add(queryFecha, BooleanClause.Occur.MUST);
                    Hits hits = searcher.Search(booleanQuery, Sort.RELEVANCE);

                    searcher.Close();
                    int idDocumento = -1;
                    for (int i = 0; i < hits.Length(); i++)
                    {
                        if (fileSystemElement.Is_File)
                        {
                            if (hits.Doc(i).GetField(IndexadorCampos.Path).StringValue() == fileSystemElement.Path &&
                                hits.Doc(i).GetField(IndexadorCampos.Nombre).StringValue() == fileSystemElement.Nombre &&
                                hits.Doc(i).GetField(IndexadorCampos.Extension).StringValue() == fileSystemElement.Extension)
                            {
                                idDocumento = hits.Id(i);
                                break;
                            }
                        }
                        else
                        {
                            if (hits.Doc(i).GetField(IndexadorCampos.Path).StringValue() == fileSystemElement.Path &&
                                hits.Doc(i).GetField(IndexadorCampos.Nombre).StringValue() == fileSystemElement.Nombre)
                            {
                                idDocumento = hits.Id(i);
                                break;
                            }
                        }
                    }
                    if (idDocumento > 0)
                    {
                        reader.DeleteDocument(idDocumento);
                        reader.Close();
                    }
                }

            }
            catch (Exception)
            {
                throw new Exception("Error al eliminar el archivo indexado.");
            }
            finally
            {
                if (searcher != null) searcher.Close();
                if (reader != null) reader.Close();
            }
        }



        #endregion

        #region "Metodos privados"

        /// <summary>
        /// Inicializa el indexador para la busqueda
        /// </summary>
        /// <param name="pathIndexador"></param>
        /// <returns></returns>
        private static IndexSearcher GetIndexSearcher(string pathIndexador)
        {
            FileInfo indexador = new FileInfo(pathIndexador);
            bool directoryExists = indexador.Directory.Exists;
            bool createDirectory = !directoryExists;
            if (_directory != null)
            {
                _directory = FSDirectory.GetDirectory(indexador, createDirectory);
            }
            if (_analyzer != null)
            {
                _analyzer = new StandardAnalyzer();
            }
            return new IndexSearcher(_directory);
        }

        /// <summary>
        /// Convertir resultados en DataSet
        /// </summary>
        /// <param name="hits"></param>
        /// <param name="resultado"></param>
        private static void TablaResultados(Hits hits, out DataSet resultado)
        {
            resultado = new DataSet();
            DataTable terminos = new DataTable();
            terminos.Columns.Add(IndexadorCampos.Nombre, typeof(String));
            terminos.Columns.Add(IndexadorCampos.Path, typeof(String));
            terminos.Columns.Add(IndexadorCampos.Extension, typeof(String));
            terminos.Columns.Add(IndexadorCampos.Fecha, typeof(String));
            terminos.Columns.Add(IndexadorCampos.Tamaño, typeof(String));
            terminos.Columns.Add(IndexadorCampos.Carpeta, typeof(String));

            for (int i = 0; i < hits.Length(); i++)
            {
                terminos.Rows.Add(hits.Doc(i).Get(IndexadorCampos.Nombre),
                                  hits.Doc(i).Get(IndexadorCampos.Path),
                                  hits.Doc(i).Get(IndexadorCampos.Extension),
                                  hits.Doc(i).Get(IndexadorCampos.Fecha),
                                  hits.Doc(i).Get(IndexadorCampos.Tamaño),
                                  hits.Doc(i).Get(IndexadorCampos.Carpeta));
            }
            resultado.Tables.Add(terminos);
        }

        /// <summary>
        /// Define los valores de inicializacion del indexador.
        /// </summary>
        /// <param name="pathIndexador"></param>
        /// <param name="indexWriter"></param>
        /// <returns></returns>
        private static Document InicializarIndexadorLucene(string pathIndexador, out IndexWriter indexWriter)
        {
            FileInfo indexador = new FileInfo(pathIndexador);
            bool directoryExists = indexador.Directory.Exists;
            bool createDirectory = !directoryExists;
            if (_directory == null)
            {
                _directory = FSDirectory.GetDirectory(indexador, createDirectory);
            }
            if (_analyzer == null)
            {
                _analyzer = new StandardAnalyzer();
            }
            bool indexExists = IndexReader.IndexExists(_directory);
            bool createIndex = !indexExists;
            indexWriter = new IndexWriter(_directory, _analyzer, createIndex);
            return new Document();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileSystemElement"></param>
        /// <param name="document"></param>
        private static void InicializarDocumento(FileSystemElement fileSystemElement, Document document)
        {
            Field extensionField = new Field(IndexadorCampos.Extension, fileSystemElement.Extension, Field.Store.YES, Field.Index.UN_TOKENIZED);
            document.Add(extensionField);
            Field nombreField = new Field(IndexadorCampos.Nombre, fileSystemElement.Nombre, Field.Store.YES, Field.Index.UN_TOKENIZED);
            document.Add(nombreField);
            Field tamañoField = new Field(IndexadorCampos.Tamaño, fileSystemElement.Tamaño.ToString(), Field.Store.YES, Field.Index.UN_TOKENIZED);
            document.Add(tamañoField);
            Field carpetaField = new Field(IndexadorCampos.Carpeta, fileSystemElement.Path, Field.Store.YES, Field.Index.UN_TOKENIZED);
            document.Add(carpetaField);
            Field pathField = new Field(IndexadorCampos.Path, fileSystemElement.Nombre, Field.Store.YES, Field.Index.UN_TOKENIZED);
            document.Add(pathField);
            Field fechaCreacionField = new Field(IndexadorCampos.Fecha, fileSystemElement.Fecha_Creacion.ToString(), Field.Store.YES, Field.Index.UN_TOKENIZED);
            document.Add(fechaCreacionField);
        }


        /// <summary>
        /// Obtiene la fecha del sistema.
        /// </summary>
        /// <returns></returns>
        private static DateTime GetFechaCreacion()
        {
            TimeMgr.TimeMgr mgr = new TimeMgr.TimeMgr();
            return mgr.ObtenerFecha();
        }

        #endregion

    }
}
